package org.juiceshop.musi.midi;

import java.util.ArrayList;
import java.util.List;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Track;

public class SequenceBuilder {

	public static final int RESOLUTION = 256/4;
	public static final int WHOLE = RESOLUTION * 4;

	public static final byte META_END_OF_TRACK = 0x2f;
	public static final byte META_SET_TEMPO = 0x51;
	public static final byte META_TIME_SIGNATURE = 0x58;
	public static final byte META_KEY_SIGNATURE = 0x59;

	private Sequence _sequence;
	private List<TrackBuilder> _trackBuilders;
	
	public SequenceBuilder() {
		try {
			_sequence = new Sequence(Sequence.PPQ, RESOLUTION);
		} catch (InvalidMidiDataException e) {
			throw new MidiException(e);
		}
		_trackBuilders = new ArrayList<TrackBuilder>();
	}
	
	public Sequence getSequence() {
		return _sequence;
	}
	
	
		
	public TrackBuilder getTrackBuilder (int trackNum) {
		assert trackNum>=0 && trackNum<16;
		while (_trackBuilders.size()<=trackNum) {
			int n = _trackBuilders.size();
			Track track = _sequence.createTrack();
			TrackBuilder tbuilder = new TrackBuilder(track,n);
			_trackBuilders.add(tbuilder);
		}
		return _trackBuilders.get(trackNum);
	}
	
	public long getMaxTicks() {
		long maxTicks = 0l;
		for (TrackBuilder tb : _trackBuilders) {
			if (tb.getTimer()>maxTicks) maxTicks = tb.getTimer();
		}
		return maxTicks;
	}
	
	public long sync() {
		long maxTicks = getMaxTicks();
		for (TrackBuilder tb : _trackBuilders) {
			tb.setTimer(maxTicks);
		}
		return maxTicks;
	}
	
	public void tempo (int bpm) {
        if (bpm<1) {
            throw new IllegalArgumentException("Invalid BPM value: "+bpm);
        }
        sync();
        // Microseconds per minute divided by BPM value
        int mpq = 60000000 / bpm;
        byte[] data = new byte[3];
        data[0] = (byte)((mpq >> 16) & 0xFF);
        data[1] = (byte)((mpq >> 8) & 0xFF);
        data[2] = (byte)(mpq & 0xFF);
        getTrackBuilder(0).metaMessage (META_SET_TEMPO, data);
    }

	public void close() {
		sync();
		for (TrackBuilder tb : _trackBuilders) {
			tb.close();
		}
	}
	
	
	public static long calculateTicks(double duration) {
        return Double.valueOf(WHOLE * duration).longValue();
    }

	
}


